﻿//----------------------------------------------------------------
// Desc: 
// Edit: ShowLong, http://blog.csdn.net/showlong
//
// Date Created: 2012-09-11
//
// Update Date : 
//
// Copyright (c) Fuel4D (http://www.fuel4d.com), All rights reserved.
//
//----------------------------------------------------------------
#ifndef __FUI_STRINGT_H__
#define __FUI_STRINGT_H__
#pragma once


#include "UI/Base/FUI_SimpleString.h"


template< typename _CharType >
class FUI_StringTraitsCRT : public IFUI_StringTraitsBase< _CharType >
{
public:
    static ACHAR* FCDECL CharNext(const ACHAR* p)
    {
        if (*p & 0x80)
            return (ACHAR*)p + 2;
        return (ACHAR*)p + 1;
    }

    static int FCDECL IsDigit(ACHAR ch)
    {
        return isdigit( ch );
    }

    static int FCDECL IsSpace(ACHAR ch)
    {
        return isspace( ch );
    }

    static int FCDECL StringCompare(const ACHAR* pszA, const ACHAR* pszB)
    {
        return FUI_StrcmpA( reinterpret_cast< const ACHAR* >( pszA ), reinterpret_cast< const ACHAR* >( pszB ) );
    }

    static int FCDECL StringCompareIgnore(ACHAR* pszA, ACHAR* pszB)
    {
        return FUI_StricmpA( reinterpret_cast< const ACHAR* >( pszA ), reinterpret_cast< const ACHAR* >( pszB ) );
    }

    static int FCDECL StringCollate(ACHAR* pszA, ACHAR* pszB)
    {
        return strcoll( reinterpret_cast< const ACHAR* >( pszA ), reinterpret_cast< const ACHAR* >( pszB ) );
    }

    static ACHAR* FCDECL StringFindString(const ACHAR* pszBlock, const ACHAR* pszMatch)
    {
        return strstr((ACHAR*)pszBlock, pszMatch);
    }

    static ACHAR* FCDECL StringFindString(ACHAR* pszBlock, ACHAR* pszMatch)
    {
        return reinterpret_cast< ACHAR* >( strstr( reinterpret_cast< const BYTE* >( pszBlock ),
            reinterpret_cast< const BYTE* >( pszMatch ) ) );
    }

    static const ACHAR* FCDECL StringFindChar(const ACHAR* pszBlock, ACHAR chMatch)
    {
#ifdef UNICODE
        return reinterpret_cast< const ACHAR* >( strchr( reinterpret_cast< const ACHAR* >( pszBlock ), (BYTE)chMatch ) );
#else
        return reinterpret_cast< const ACHAR* >( strchr( pszBlock, (BYTE)chMatch ) );
#endif
    }

    static const ACHAR* FCDECL StringFindCharRev(const ACHAR* psz, ACHAR ch)
    {
        return reinterpret_cast< const ACHAR* >( strrchr( reinterpret_cast< const ACHAR* >( psz ), (int)ch ) );
    }

    static ACHAR* FCDECL StringScanSet(ACHAR* pszBlock, ACHAR* pszMatch)
    {
        return reinterpret_cast< ACHAR* >( strpbrk( reinterpret_cast< const BYTE* >( pszBlock ),
            reinterpret_cast< const BYTE* >( pszMatch ) ) );
    }

    static int FCDECL StringSpanIncluding(ACHAR* pszBlock, ACHAR* pszSet)
    {
        return (int)strspn( reinterpret_cast< const BYTE* >( pszBlock ), reinterpret_cast< const BYTE* >( pszSet ) );
    }

    static int FCDECL StringSpanExcluding(ACHAR* pszBlock, ACHAR* pszSet)
    {
        return (int)strcspn( reinterpret_cast< const BYTE* >( pszBlock ), reinterpret_cast< const BYTE* >( pszSet ) );
    }

    static ACHAR* FCDECL StringUppercase(ACHAR* psz)
    {
        return FUI_StrToUpperA(psz);
    }

    static ACHAR* FCDECL StringLowercase(ACHAR* psz)
    {
        return FUI_StrToLowerA(psz);
    }

    static ACHAR* FCDECL StringUppercase(ACHAR* psz, size_t size)
    {
        return FUI_StrToUpperA(psz);
    }

    static ACHAR* FCDECL StringLowercase(ACHAR* psz, size_t size)
    {
        return FUI_StrToLowerA(psz);
    }

    static int FCDECL GetFormattedLength(const ACHAR* pszFormat, va_list args)
    {
#if _MSC_VER > 1200
        return _vscprintf( pszFormat, args );
#else
        return vprintf( pszFormat, args );
#endif
    }

    static int FCDECL Format(ACHAR* pszBuffer, const ACHAR* pszFormat, va_list args )
    {
        return vsprintf( pszBuffer, pszFormat, args );
    }
    static int FCDECL Format(ACHAR* pszBuffer, size_t nlength, const ACHAR* pszFormat, va_list args )
    {
#if _MSC_VER > 1200
        return vsprintf_s( pszBuffer, nlength, pszFormat, args );
#else
        return vsprintf( pszBuffer, pszFormat, args );
#endif
    }

    static int FCDECL GetBaseTypeLength(const ACHAR* pszSrc)
    {
        // Returns required buffer length in XCHARs
        return int( FUI_StrlenA( pszSrc ) );
    }

    static int FCDECL GetBaseTypeLength(const ACHAR* pszSrc, int nLength)
    {
        (void)pszSrc;
        // Returns required buffer length in XCHARs
        return nLength;
    }

    static int FCDECL GetBaseTypeLength(const WCHAR* pszSource)
    {
        // Returns required buffer length in XCHARs
        return FUI_AStrLength(pszSource);
    }

    static int FCDECL GetBaseTypeLength(const WCHAR* pszSource, int nLength)
    {
        // Returns required buffer length in XCHARs
        return FUI_AStrLength(pszSource);
    }

    static void FCDECL ConvertToBaseType(
        ACHAR* pszDest,  int nDestLength,
        ACHAR* pszSrc,   int nSrcLength = -1)
    {
        if (nSrcLength == -1) { nSrcLength=1 + GetBaseTypeLength(pszSrc); }
        // nLen is in XCHARs
#if _MSC_VER > 1200
        memcpy_s( pszDest, nDestLength*sizeof( ACHAR ),
            pszSrc, nSrcLength*sizeof( ACHAR ) );
#else
        memcpy( pszDest,
            pszSrc, nSrcLength*sizeof( ACHAR ) );
#endif
    }

    static void FCDECL ConvertToBaseType(
        ACHAR* pszDest,        int nDestLength,
        const WCHAR* pszSrc,   int nSrcLength = -1)
    {
        FUI_WStrToAStrNS(pszSrc, pszDest);
    }

    static void FCDECL FloodCharacters(ACHAR ch, int nLength, ACHAR* pch)
    {
        // nLength is in XCHARs
        memset( pch, ch, nLength );
    }

    static int FCDECL SafeStringLen(const ACHAR* psz)
    {
        // returns length in bytes
        return (psz != NULL) ? int( FUI_StrlenA( psz ) ) : 0;
    }

    static int FCDECL SafeStringLen(const WCHAR* psz)
    {
        // returns length in wchar_ts
        return (psz != NULL) ? int( FUI_StrlenW( psz ) ) : 0;
    }
};

// specialization for WCHAR
template<>
class FUI_StringTraitsCRT< WCHAR > :
    public IFUI_StringTraitsBase< WCHAR >
{
public:
    static WCHAR* FCDECL CharNext(const WCHAR* psz)
    {
        return const_cast< WCHAR* >( psz+1 );
    }

    static int FCDECL IsDigit(WCHAR ch)
    {
        return iswdigit( static_cast<unsigned short>(ch) );
    }

    static int FCDECL IsSpace(WCHAR ch)
    {
        return iswspace( static_cast<unsigned short>(ch) );
    }

    static int FCDECL StringCompare(const WCHAR* pszA, const WCHAR* pszB)
    {
        return FUI_StrcmpW( pszA, pszB );
    }

    static int FCDECL StringCompareIgnore(const WCHAR* pszA, const WCHAR* pszB)
    {
        return FUI_StricmpW( pszA, pszB );
    }

    static int FCDECL StringCollate(const WCHAR* pszA, const WCHAR* pszB)
    {
        return wcscoll( pszA, pszB );
    }

    static const WCHAR* FCDECL StringFindString(const WCHAR* pszBlock, const WCHAR* pszMatch)
    {
        return wcsstr( pszBlock, pszMatch );
    }

    static WCHAR* FCDECL StringFindString(WCHAR* pszBlock, const WCHAR* pszMatch)
    {
        return( const_cast< WCHAR* >( StringFindString( const_cast< const WCHAR* >( pszBlock ), pszMatch ) ) );
    }

    static const WCHAR* FCDECL StringFindChar(const WCHAR* pszBlock, WCHAR chMatch)
    {
        return wcschr( pszBlock, chMatch );
    }

    static const WCHAR* FCDECL StringFindCharRev(const WCHAR* psz, WCHAR ch)
    {
        return wcsrchr( psz, ch );
    }

    static const WCHAR* FCDECL StringScanSet(const WCHAR* pszBlock, const WCHAR* pszMatch)
    {
        return wcspbrk( pszBlock, pszMatch );
    }

    static int FCDECL StringSpanIncluding(const WCHAR* pszBlock, const WCHAR* pszSet)
    {
        return (int)wcsspn( pszBlock, pszSet );
    }

    static int FCDECL StringSpanExcluding(const WCHAR* pszBlock, const WCHAR* pszSet)
    {
        return (int)wcscspn( pszBlock, pszSet );
    }

    static WCHAR* FCDECL StringUppercase(WCHAR* psz)
    {
        return FUI_StrToUpperW(psz);
    }

    static WCHAR* FCDECL StringLowercase(WCHAR* psz)
    {
        return FUI_StrToLowerW(psz);
    }

    static WCHAR* FCDECL StringUppercase(WCHAR* psz, size_t size)
    {
        return FUI_StrToUpperW(psz);
    }

    static WCHAR* FCDECL StringLowercase(WCHAR* psz, size_t size)
    {
        return FUI_StrToLowerW(psz);
    }

    static int FCDECL GetFormattedLength(const WCHAR* pszFormat, va_list args)
    {
#if _MSC_VER > 1200
        return _vscwprintf( pszFormat, args );
#else
        return vwprintf( pszFormat, args );
#endif
    }
    static int FCDECL Format(WCHAR* pszBuffer, size_t nLength, const WCHAR* pszFormat, va_list args)
    {
        return vswprintf( pszBuffer, nLength, pszFormat, args );
    }

    static int FCDECL GetBaseTypeLength(const ACHAR* pszSrc)
    {
        // Returns required buffer size in wchar_ts
        return FUI_WStrLength(pszSrc);
    }

    static int FCDECL GetBaseTypeLength(const ACHAR* pszSrc, int nLength)
    {
        // Returns required buffer size in wchar_ts
        return FUI_WStrLength(pszSrc);
    }

    static int FCDECL GetBaseTypeLength(const WCHAR* pszSrc)
    {
        // Returns required buffer size in wchar_ts
        return (int)FUI_StrlenW( pszSrc );
    }

    static int FCDECL GetBaseTypeLength(const WCHAR* pszSrc, int nLength)
    {
        (void)pszSrc;
        // Returns required buffer size in wchar_ts
        return nLength;
    }

    static void FCDECL ConvertToBaseType(
        WCHAR* pszDest, int nDestLength,
        const ACHAR* pszSrc, int nSrcLength = -1)
    {
        FUI_AStrToWStrNS(pszSrc, pszDest);
    }

    static void FCDECL ConvertToBaseType(
        WCHAR* pszDest,
        int nDestLength,
        const WCHAR* pszSrc,
        int nSrcLength = -1 )
    {
        if (nSrcLength == -1) { nSrcLength=1 + GetBaseTypeLength(pszSrc); }
        // nLen is in wchar_ts
#if _MSC_VER > 1200
        wmemcpy_s(pszDest, nDestLength, pszSrc, nSrcLength);
#else
        wmemcpy(pszDest, pszSrc, nSrcLength);
#endif
    }

    static void FCDECL FloodCharacters(WCHAR ch, int nLength, WCHAR* psz)
    {
        // nLength is in XCHARs
        for( int i = 0; i < nLength; i++ )
            psz[i] = ch;
    }

    static int FCDECL SafeStringLen(const ACHAR* psz)
    {
        // returns length in bytes
        return (psz != NULL) ? (int)FUI_StrlenA( psz ) : 0;
    }

    static int FCDECL SafeStringLen(const WCHAR* psz)
    {
        // returns length in wchar_ts
        return (psz != NULL) ? (int)FUI_StrlenW( psz ) : 0;
    }
};


template< typename BaseType, class StringTraits >
class FUI_StringT : public FUI_SimpleStringT< BaseType >
{
public:
	typedef FUI_SimpleStringT< BaseType > _BaseClass;
    typedef IFUI_StringAllocator _IStrAllocate;
    typedef FUI_StringT _StrClass;
    typedef StringTraits StrTraits;
	
    typedef typename _BaseClass::XCHAR XCHAR;
    typedef typename _BaseClass::PXSTR PXSTR;
    typedef typename _BaseClass::PCXSTR PCXSTR;
    typedef typename _BaseClass::YCHAR YCHAR;
    typedef typename _BaseClass::PYSTR PYSTR;
    typedef typename _BaseClass::PCYSTR PCYSTR;

public:
    FUI_StringT() :
      _BaseClass( FUI_GetStringAllocator() )
      {
      }
      FUI_StringT(_IStrAllocate* pStringMgr) :
      _BaseClass( pStringMgr )
      {
      }

      static void FCDECL Construct(_StrClass* pString)
      {
          FUI_NewPT( pString, _StrClass);
      }

      // Copy constructor
      FUI_StringT(const FUI_StringT& strSrc) :
      _BaseClass( strSrc )
      {
      }

      void pop_back()
      {
          if (_BaseClass::size() > 0)
            Delete(_BaseClass::size() - 1, 1);
      }

      operator FUI_SimpleStringT<BaseType>&()
      {
          return *(FUI_SimpleStringT<BaseType>*)this;
      }

      FUI_StringT(const FUI_SimpleStringT<BaseType>& strSrc) :
      _BaseClass( strSrc )
      {
      }

      FUI_StringT(const XCHAR* pszSrc) :
      _BaseClass( FUI_GetStringAllocator() )
      {
          if( !CheckImplicitLoad( pszSrc ) )
          {
              *this = pszSrc;
          }
      }

      FUI_StringT(const XCHAR* pszSrc, _IStrAllocate* pStringMgr) :
      _BaseClass( pStringMgr )
      {
          if( !CheckImplicitLoad( pszSrc ) )
          {
              *this = pszSrc;
          }
      }

      FUI_StringT(const YCHAR* pszSrc) :
      _BaseClass( FUI_GetStringAllocator() )
      {
          if( !CheckImplicitLoad( pszSrc ) )
          {
              *this = pszSrc;
          }
      }

      FUI_StringT(const YCHAR* pszSrc, _IStrAllocate* pStringMgr) :
      _BaseClass( pStringMgr )
      {
          if( !CheckImplicitLoad( pszSrc ) )
          {
              *this = pszSrc;
          }
      }

      FUI_StringT(const BYTE* pszSrc) :
      _BaseClass( FUI_GetStringAllocator() )
      {
          *this = reinterpret_cast< const ACHAR* >( pszSrc );
      }

      FUI_StringT(const BYTE* pszSrc, _IStrAllocate* pStringMgr) :
      _BaseClass( pStringMgr )
      {
          *this = reinterpret_cast< const ACHAR* >( pszSrc );
      }

#define FSTRING_CHAR_T ACHAR
      FUI_StringT(FSTRING_CHAR_T ch, int nLength = 1) :
      _BaseClass( FUI_GetStringAllocator() )
      {
          if( nLength > 0 )
          {
              PXSTR pszBuffer = _BaseClass::GetBuffer( nLength );
              StringTraits::FloodCharacters( XCHAR( ch ), nLength, pszBuffer );
              _BaseClass::ReleaseBufferSetLength( nLength );
          }
      }
#undef FSTRING_CHAR_T

      template< typename T, int t_nFixedBytes = 128 >
      class FCacheBuffer
      {
      public:
          FCacheBuffer() :
            m_p( NULL )
            {
            }
            FCacheBuffer(size_t nElements)
                :m_p( NULL )
            {
                Allocate( nElements );
            }

            ~FCacheBuffer()
            {
                if( m_p != reinterpret_cast< T* >( m_abFixedBuffer ) )
                {
                    FreeHeap();
                }
            }

            operator T*() const
            {
                return( m_p );
            }
            T* operator->() const
            {
                return( m_p );
            }

            T* Allocate(size_t nElements)
            {
                return( AllocateBytes( nElements *sizeof( T ) ) );
            }

            T* Reallocate(size_t nElements)
            {
                size_t nNewSize = nElements*sizeof( T ) ;

                if (m_p == NULL)
                    return AllocateBytes(nNewSize);

                if (nNewSize > t_nFixedBytes)
                {
                    if( m_p == reinterpret_cast< T* >( m_abFixedBuffer ) )
                    {
                        // We have to allocate from the heap and copy the contents into the buffer
                        AllocateHeap(nNewSize);
#if _MSC_VER > 1200
                        memcpy_s(m_p, nNewSize, m_abFixedBuffer, t_nFixedBytes);
#else
                        memcpy(m_p, m_abFixedBuffer, t_nFixedBytes);
#endif
                    }
                    else
                    {
                        ReAllocateHeap( nNewSize );
                    }
                }
                else
                {
                    if (m_p != reinterpret_cast< T* >( m_abFixedBuffer ))
                    {
#if _MSC_VER > 1200
                        memcpy_s(m_abFixedBuffer, t_nFixedBytes, m_p, nNewSize);
#else
                        memcpy(m_abFixedBuffer, m_p, nNewSize);
#endif
                        FreeHeap();
                    }
                    m_p = reinterpret_cast< T* >( m_abFixedBuffer );
                }

                return m_p;
            }

            T* AllocateBytes(size_t nBytes)
            {
                if( nBytes > t_nFixedBytes )
                {
                    AllocateHeap( nBytes );
                }
                else
                {
                    m_p = reinterpret_cast< T* >( m_abFixedBuffer );
                }

                return( m_p );
            }

      private:
          void AllocateHeap(size_t nBytes)
          {
              T* p = (T*)( FUI_Alloc(BYTE, nBytes ) );
              m_p = p;
          }

          void ReAllocateHeap(size_t nNewSize)
          {
              T* p = (T*)( FUI_Realloc(m_p, BYTE, nNewSize) );
              m_p = p;
          }

          void FreeHeap()
          {
              FUI_Free( m_p );
          }

      private:
          T* m_p;
          BYTE m_abFixedBuffer[t_nFixedBytes];
      };

      FUI_StringT(
          WCHAR ch,
          int nLength = 1) :
      _BaseClass( FUI_GetStringAllocator() )
      {
          if( nLength > 0 )
          {
              //Convert ch to the BaseType
              WCHAR pszCh[2] = { ch , 0 };
              int nBaseTypeCharLen = 1;

              if(ch != _FENDW)
              {
                  nBaseTypeCharLen = StringTraits::GetBaseTypeLength(pszCh);
              }

              FCacheBuffer<XCHAR,10> buffBaseTypeChar;
              buffBaseTypeChar.Allocate(nBaseTypeCharLen+1);
              StringTraits::ConvertToBaseType( buffBaseTypeChar, nBaseTypeCharLen+1, pszCh, 1 );
              //Allocate enough characters in String and flood (replicate) with the (converted character)*nLength
              PXSTR pszBuffer = _BaseClass::GetBuffer( nLength*nBaseTypeCharLen );
              if (nBaseTypeCharLen == 1)
              {   //Optimization for a common case - wide ACHAR translates to 1 ansi/wide ACHAR.
                  StringTraits::FloodCharacters( buffBaseTypeChar[0], nLength, pszBuffer );
              } else
              {
                  XCHAR* p=pszBuffer;
                  for (int i=0 ; i < nLength ;++i)
                  {
                      for (int j=0 ; j < nBaseTypeCharLen ;++j)
                      {
                          *p=buffBaseTypeChar[j];
                          ++p;
                      }
                  }
              }
              _BaseClass::ReleaseBufferSetLength( nLength*nBaseTypeCharLen );
          }
      }

      FUI_StringT(
          const XCHAR* pch,
          int nLength) :
      _BaseClass( pch, nLength, FUI_GetStringAllocator() )
      {
      }

      FUI_StringT(
          const XCHAR* pch,
          int nLength,
          _IStrAllocate* pStringMgr) :
      _BaseClass( pch, nLength, pStringMgr )
      {
      }

      FUI_StringT(
          const YCHAR* pch,
          int nLength) :
      _BaseClass( FUI_GetStringAllocator() )
      {
          if( nLength > 0 )
          {
              int nDestLength = StringTraits::GetBaseTypeLength( pch, nLength );
              PXSTR pszBuffer = _BaseClass::GetBuffer( nDestLength );
              StringTraits::ConvertToBaseType( pszBuffer, nDestLength, pch, nLength );
              _BaseClass::ReleaseBufferSetLength( nDestLength );
          }
      }

      FUI_StringT(
          const YCHAR* pch,
          int nLength,
          _IStrAllocate* pStringMgr) :
      _BaseClass( pStringMgr )
      {
          if( nLength > 0 )
          {
              int nDestLength = StringTraits::GetBaseTypeLength( pch, nLength );
              PXSTR pszBuffer = _BaseClass::GetBuffer( nDestLength );
              StringTraits::ConvertToBaseType( pszBuffer, nDestLength, pch, nLength );
              _BaseClass::ReleaseBufferSetLength( nDestLength );
          }
      }

      // Destructor
      ~FUI_StringT()
      {
      }

      // Assignment operators
      _StrClass& operator=(const _StrClass& strSrc)
      {
          _BaseClass::operator=( strSrc );

          return( *this );
      }

      _StrClass& operator=(const FUI_SimpleStringT<BaseType>& strSrc)
      {
          _BaseClass::operator=( strSrc );

          return( *this );
      }

      _StrClass& operator=(const PCXSTR pszSrc)
      {
          _BaseClass::operator=( pszSrc );

          return( *this );
      }

      _StrClass& operator=(const PCYSTR pszSrc)
      {
          // nDestLength is in XCHARs
          int nDestLength = (pszSrc != NULL) ? StringTraits::GetBaseTypeLength( pszSrc ) : 0;
          if( nDestLength > 0 )
          {
              PXSTR pszBuffer = _BaseClass::GetBuffer( nDestLength );
              StringTraits::ConvertToBaseType( pszBuffer, nDestLength, pszSrc);
              _BaseClass::ReleaseBufferSetLength( nDestLength );
          }
          else
          {
        	  _BaseClass::Empty();
          }

          return( *this );
      }

      _StrClass& operator=(const BYTE* pszSrc)
      {
          return( operator=( reinterpret_cast< const ACHAR* >( pszSrc ) ) );
      }

      _StrClass& operator=(XCHAR ch)
      {
          XCHAR ach[2] = { ch, 0 };

          return( operator=( ach ) );
      }

      _StrClass& operator=(YCHAR ch)
      {
          YCHAR ach[2] = { ch, 0 };

          return( operator=( ach ) );
      }

      _StrClass& operator+=(const _BaseClass& str)
      {
          _BaseClass::operator+=( str );

          return( *this );
      }
      _StrClass& operator+=(PCXSTR pszSrc)
      {
          _BaseClass::operator+=( pszSrc );

          return( *this );
      }

      _StrClass& operator+=(PCYSTR pszSrc)
      {
          _StrClass str( pszSrc, GetManager() );

          return( operator+=( str ) );
      }

      _StrClass& operator+=(XCHAR ch)
      {
          _BaseClass::operator+=( ch );

          return( *this );
      }

      // Override from base class
      _IStrAllocate* GetManager() const
      {
          _IStrAllocate* pStringMgr = _BaseClass::GetManager();
          if(pStringMgr) { return pStringMgr; }

          pStringMgr = FUI_GetStringAllocator();
          return pStringMgr->Clone();
      }

      // Comparison

      int Compare(PCXSTR psz) const
      {
          return( StringTraits::StringCompare( _BaseClass::GetString(), psz ) );
      }

      int CompareNoCase(PCXSTR psz) const
      {
          return( StringTraits::StringCompareIgnore( _BaseClass::GetString(), psz ) );
      }

      int Collate(PCXSTR psz) const
      {
          return( StringTraits::StringCollate( _BaseClass::GetString(), psz ) );
      }

      // Advanced manipulation

      // Delete 'nCount' characters, starting at index 'iIndex'
      int Delete(
          int iIndex,
          int nCount = 1)
      {
          if( iIndex < 0 )
              iIndex = 0;

          if( nCount < 0 )
              nCount = 0;

          int nLength = _BaseClass::GetLength();
          if( nCount + iIndex > nLength )
          {
              nCount = nLength-iIndex;
          }
          if( nCount > 0 )
          {
              int nNewLength = nLength-nCount;
              int nXCHARsToCopy = nLength-(iIndex+nCount)+1;
              PXSTR pszBuffer = _BaseClass::GetBuffer();
#if _MSC_VER > 1200
              memmove_s( pszBuffer+iIndex, nXCHARsToCopy*sizeof( XCHAR ),
                  pszBuffer+iIndex+nCount, nXCHARsToCopy*sizeof( XCHAR ) );
#else
              memmove( pszBuffer+iIndex,
                  pszBuffer+iIndex+nCount, nXCHARsToCopy*sizeof( XCHAR ) );
#endif
              _BaseClass::ReleaseBufferSetLength( nNewLength );
          }

          return( _BaseClass::GetLength() );
      }

      // Insert character 'ch' before index 'iIndex'
      int Insert(
          int iIndex,
          XCHAR ch)
      {
          if( iIndex < 0 )
              iIndex = 0;

          if( iIndex > _BaseClass::GetLength() )
          {
              iIndex = _BaseClass::GetLength();
          }
          int nNewLength = _BaseClass::GetLength()+1;

          PXSTR pszBuffer = _BaseClass::GetBuffer( nNewLength );

          // move existing bytes down
#if _MSC_VER > 1200
          memmove_s( pszBuffer+iIndex+1, (nNewLength-iIndex)*sizeof( XCHAR ),
              pszBuffer+iIndex, (nNewLength-iIndex)*sizeof( XCHAR ) );
#else
          memmove( pszBuffer+iIndex+1,
              pszBuffer+iIndex, (nNewLength-iIndex)*sizeof( XCHAR ) );
#endif
          pszBuffer[iIndex] = ch;

          _BaseClass::ReleaseBufferSetLength( nNewLength );

          return( nNewLength );
      }

      // Insert string 'psz' before index 'iIndex'
      int Insert(
          int iIndex,
          PCXSTR psz)
      {
          if( iIndex < 0 )
              iIndex = 0;

          if( iIndex > _BaseClass::GetLength() )
          {
              iIndex = _BaseClass::GetLength();
          }

          // nInsertLength and nNewLength are in XCHARs
          int nInsertLength = StringTraits::SafeStringLen( psz );
          int nNewLength = _BaseClass::GetLength();
          if( nInsertLength > 0 )
          {
              nNewLength += nInsertLength;

              PXSTR pszBuffer = _BaseClass::GetBuffer( nNewLength );
              // move existing bytes down
#if _MSC_VER > 1200
              memmove_s( pszBuffer+iIndex+nInsertLength, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ),
                  pszBuffer+iIndex, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ) );
              memcpy_s( pszBuffer+iIndex, nInsertLength*sizeof( XCHAR ),
                  psz, nInsertLength*sizeof( XCHAR ) );
#else
              memmove( pszBuffer+iIndex+nInsertLength,
                  pszBuffer+iIndex, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ) );
              memcpy( pszBuffer+iIndex,
                  psz, nInsertLength*sizeof( XCHAR ) );
#endif
              _BaseClass::ReleaseBufferSetLength( nNewLength );
          }

          return( nNewLength );
      }

      // Insert string 'psz' before index 'iIndex'
      int Insert(
          int iIndex,
          PCXSTR psz,
          int nInsertLength)
      {
          if( iIndex < 0 )
              iIndex = 0;

          if( iIndex > _BaseClass::GetLength() )
          {
              iIndex = _BaseClass::GetLength();
          }

          // nInsertLength and nNewLength are in XCHARs
          int nNewLength = _BaseClass::GetLength();
          if( nInsertLength > 0 )
          {
              nNewLength += nInsertLength;

              PXSTR pszBuffer = _BaseClass::GetBuffer( nNewLength );
              // move existing bytes down
#if _MSC_VER > 1200
              memmove_s( pszBuffer+iIndex+nInsertLength, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ),
                  pszBuffer+iIndex, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ) );
              memcpy_s( pszBuffer+iIndex, nInsertLength*sizeof( XCHAR ),
                  psz, nInsertLength*sizeof( XCHAR ) );
#else
              memmove( pszBuffer+iIndex+nInsertLength,
                  pszBuffer+iIndex, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ) );
              memcpy( pszBuffer+iIndex,
                  psz, nInsertLength*sizeof( XCHAR ) );
#endif
              _BaseClass::ReleaseBufferSetLength( nNewLength );
          }

          return( nNewLength );
      }

      // Replace all occurrences of character 'chOld' with character 'chNew'
      int Replace(
          XCHAR chOld,
          XCHAR chNew)
      {
          int nCount = 0;

          // short-circuit the nop case
          if( chOld != chNew )
          {
              // otherwise modify each character that matches in the string
              bool bCopied = false;
              PXSTR pszBuffer = const_cast< PXSTR >( _BaseClass::GetString() );  // We don't actually write to pszBuffer until we've called _BaseClass::GetBuffer().

              int nLength = _BaseClass::GetLength();
              int iChar = 0;
              while( iChar < nLength )
              {
                  // replace instances of the specified character only
                  if( pszBuffer[iChar] == chOld )
                  {
                      if( !bCopied )
                      {
                          bCopied = true;
                          pszBuffer = _BaseClass::GetBuffer( nLength );
                      }
                      pszBuffer[iChar] = chNew;
                      nCount++;
                  }
                  iChar = int( StringTraits::CharNext( pszBuffer+iChar )-pszBuffer );
              }
              if( bCopied )
              {
                  _BaseClass::ReleaseBufferSetLength( nLength );
              }
          }

          return( nCount );
      }

      // Replace all occurrences of string 'pszOld' with string 'pszNew'
      int Replace(
          PCXSTR pszOld,
          PCXSTR pszNew)
      {
          // can't have empty or NULL lpszOld

          // nSourceLen is in XCHARs
          int nSourceLen = StringTraits::SafeStringLen( pszOld );
          if( nSourceLen == 0 )
              return( 0 );
          // nReplacementLen is in XCHARs
          int nReplacementLen = StringTraits::SafeStringLen( pszNew );

          // loop once to figure out the size of the result string
          int nCount = 0;
          {
              PCXSTR pszStart = _BaseClass::GetString();
              PCXSTR pszEnd = pszStart+_BaseClass::GetLength();
              while( pszStart < pszEnd )
              {
                  PCXSTR pszTarget;
                  while( (pszTarget = StringTraits::StringFindString( pszStart, pszOld ) ) != NULL)
                  {
                      nCount++;
                      pszStart = pszTarget+nSourceLen;
                  }
                  pszStart += StringTraits::SafeStringLen( pszStart )+1;
              }
          }

          // if any changes were made, make them
          if( nCount > 0 )
          {
              // if the buffer is too small, just
              //   allocate a buffer (slow but sure)
              int nOldLength = _BaseClass::GetLength();
              int nNewLength = nOldLength+(nReplacementLen-nSourceLen)*nCount;

              PXSTR pszBuffer = _BaseClass::GetBuffer( ( nNewLength > nOldLength ) ? nNewLength : nOldLength);

              PXSTR pszStart = pszBuffer;
              PXSTR pszEnd = pszStart+nOldLength;

              // loop again to actually do the work
              while( pszStart < pszEnd )
              {
                  PXSTR pszTarget;
                  while( (pszTarget = StringTraits::StringFindString( pszStart, pszOld ) ) != NULL )
                  {
                      int nBalance = nOldLength-int(pszTarget-pszBuffer+nSourceLen);
#if _MSC_VER > 1200
                      memmove_s( pszTarget+nReplacementLen, nBalance*sizeof( XCHAR ),
                          pszTarget+nSourceLen, nBalance*sizeof( XCHAR ) );
                      memcpy_s( pszTarget, nReplacementLen*sizeof( XCHAR ),
                          pszNew, nReplacementLen*sizeof( XCHAR ) );
#else
                      memmove( pszTarget+nReplacementLen,
                          pszTarget+nSourceLen, nBalance*sizeof( XCHAR ) );
                      memcpy( pszTarget,
                          pszNew, nReplacementLen*sizeof( XCHAR ) );
#endif
                      pszStart = pszTarget+nReplacementLen;
                      pszTarget[nReplacementLen+nBalance] = 0;
                      nOldLength += (nReplacementLen-nSourceLen);
                  }
                  pszStart += StringTraits::SafeStringLen( pszStart )+1;
              }
              _BaseClass::ReleaseBufferSetLength( nNewLength );
          }

          return( nCount );
      }

      // Remove all occurrences of character 'chRemove'
      int Remove(XCHAR chRemove)
      {
          int nLength = _BaseClass::GetLength();
          PXSTR pszBuffer = _BaseClass::GetBuffer( nLength );

          PXSTR pszSource = pszBuffer;
          PXSTR pszDest = pszBuffer;
          PXSTR pszEnd = pszBuffer+nLength;

          while( pszSource < pszEnd )
          {
              PXSTR pszNewSource = StringTraits::CharNext( pszSource );
              if( *pszSource != chRemove )
              {
                  // Copy the source to the destination.  Remember to copy all bytes of an MBCS character
                  // Copy the source to the destination.  Remember to copy all bytes of an MBCS character
                  size_t NewSourceGap = (pszNewSource-pszSource);
                  PXSTR pszNewDest = pszDest + NewSourceGap;
                  size_t i = 0;
                  for (i = 0;  pszDest != pszNewDest && i < NewSourceGap; i++)
                  {
                      *pszDest = *pszSource;
                      pszSource++;
                      pszDest++;
                  }
              }
              pszSource = pszNewSource;
          }
          *pszDest = 0;
          int nCount = int( pszSource-pszDest );
          _BaseClass::ReleaseBufferSetLength( nLength-nCount );

          return( nCount );
      }

      _StrClass Tokenize(
          PCXSTR pszTokens,
          int& iStart) const
      {
          if( (pszTokens == NULL) || (*pszTokens == (XCHAR)0) )
          {
              if (iStart < _BaseClass::GetLength())
              {
                  return( _StrClass( _BaseClass::GetString()+iStart, GetManager() ) );
              }
          }
          else
          {
              PCXSTR pszPlace = _BaseClass::GetString()+iStart;
              PCXSTR pszEnd = _BaseClass::GetString()+_BaseClass::GetLength();
              if( pszPlace < pszEnd )
              {
                  int nIncluding = StringTraits::StringSpanIncluding( pszPlace,
                      pszTokens );

                  if( (pszPlace+nIncluding) < pszEnd )
                  {
                      pszPlace += nIncluding;
                      int nExcluding = StringTraits::StringSpanExcluding( pszPlace, pszTokens );

                      int iFrom = iStart+nIncluding;
                      int nUntil = nExcluding;
                      iStart = iFrom+nUntil+1;

                      return( Mid( iFrom, nUntil ) );
                  }
              }
          }

          // return empty string, done tokenizing
          iStart = -1;

          return( _StrClass( GetManager() ) );
      }

      // find routines

      // Find the first occurrence of character 'ch', starting at index 'iStart'
      int Find(
          XCHAR ch,
          int iStart = 0) const
      {
          // nLength is in XCHARs
          int nLength = _BaseClass::GetLength();
          if( iStart < 0 || iStart >= nLength)
          {
              return( -1 );
          }

          // find first single character
          PCXSTR psz = StringTraits::StringFindChar( _BaseClass::GetString()+iStart, ch );

          // return -1 if not found and index otherwise
          return( (psz == NULL) ? -1 : int( psz-_BaseClass::GetString() ) );
      }

      // look for a specific sub-string

      // Find the first occurrence of string 'pszSub', starting at index 'iStart'
      int Find(
          PCXSTR pszSub,
          int iStart = 0) const
      {
          if(pszSub == NULL)
          {
              return( -1 );
          }
          // nLength is in XCHARs
          int nLength = _BaseClass::GetLength();
          if( iStart < 0 || iStart > nLength )
          {
              return( -1 );
          }

          // find first matching substring
          PCXSTR psz = StringTraits::StringFindString( _BaseClass::GetString()+iStart, pszSub );

          // return -1 for not found, distance from beginning otherwise
          return( (psz == NULL) ? -1 : int( psz-_BaseClass::GetString() ) );
      }

      // Find the first occurrence of any of the characters in string 'pszCharSet'
      int FindOneOf(PCXSTR pszCharSet) const
      {
          PCXSTR psz = StringTraits::StringScanSet( _BaseClass::GetString(), pszCharSet );
          return( (psz == NULL) ? -1 : int( psz-_BaseClass::GetString() ) );
      }

      // Find the last occurrence of character 'ch'
      int ReverseFind(XCHAR ch) const
      {
          // find last single character
          PCXSTR psz = StringTraits::StringFindCharRev( _BaseClass::GetString(), ch );

          // return -1 if not found, distance from beginning otherwise
          return( (psz == NULL) ? -1 : int( psz-_BaseClass::GetString() ) );
      }

      // manipulation

      // Convert the string to uppercase
      _StrClass& MakeUpper()
      {
          int nLength = _BaseClass::GetLength();
          PXSTR pszBuffer = _BaseClass::GetBuffer( nLength );
          StringTraits::StringUppercase( pszBuffer, nLength+1 );
          _BaseClass::ReleaseBufferSetLength( nLength );

          return( *this );
      }

      // Convert the string to lowercase
      _StrClass& MakeLower()
      {
          int nLength = _BaseClass::GetLength();
          PXSTR pszBuffer = _BaseClass::GetBuffer( nLength );
          StringTraits::StringLowercase( pszBuffer, nLength+1 );
          _BaseClass::ReleaseBufferSetLength( nLength );

          return( *this );
      }

      // trimming

      // Remove all trailing whitespace
      _StrClass& TrimRight()
      {
          // find beginning of trailing spaces by starting
          // at beginning (DBCS aware)

          PCXSTR psz = _BaseClass::GetString();
          PCXSTR pszLast = NULL;

          while( *psz != 0 )
          {
              if( StringTraits::IsSpace( *psz ) )
              {
                  if( pszLast == NULL )
                      pszLast = psz;
              }
              else
              {
                  pszLast = NULL;
              }
              psz = StringTraits::CharNext( psz );
          }

          if( pszLast != NULL )
          {
              // truncate at trailing space start
              int iLast = int( pszLast-_BaseClass::GetString() );

              _BaseClass::Truncate( iLast );
          }

          return( *this );
      }

      // Remove all leading whitespace
      _StrClass& TrimLeft()
      {
          // find first non-space character

          PCXSTR psz = _BaseClass::GetString();

          while( StringTraits::IsSpace( *psz ) )
          {
              psz = StringTraits::CharNext( psz );
          }

          if( psz != _BaseClass::GetString() )
          {
              // fix up data and length
              int iFirst = int( psz-_BaseClass::GetString() );
              PXSTR pszBuffer = _BaseClass::GetBuffer( _BaseClass::GetLength() );
              psz = pszBuffer+iFirst;
              int nDataLength = _BaseClass::GetLength()-iFirst;
#if _MSC_VER > 1200
              memmove_s( pszBuffer, (_BaseClass::GetLength()+1)*sizeof( XCHAR ),
                  psz, (nDataLength+1)*sizeof( XCHAR ) );
#else
              memmove( pszBuffer,
                  psz, (nDataLength+1)*sizeof( XCHAR ) );
#endif
              _BaseClass::ReleaseBufferSetLength( nDataLength );
          }

          return( *this );
      }

      // Remove all leading and trailing whitespace
      _StrClass& Trim()
      {
          return( TrimRight().TrimLeft() );
      }

      // Remove all leading and trailing occurrences of character 'chTarget'
      _StrClass& Trim(XCHAR chTarget)
      {
          return( TrimRight( chTarget ).TrimLeft( chTarget ) );
      }

      // Remove all leading and trailing occurrences of any of the characters in the string 'pszTargets'
      _StrClass& Trim(PCXSTR pszTargets)
      {
          return( TrimRight( pszTargets ).TrimLeft( pszTargets ) );
      }

      // trimming anything (either side)

      // Remove all trailing occurrences of character 'chTarget'
      _StrClass& TrimRight(XCHAR chTarget)
      {
          // find beginning of trailing matches
          // by starting at beginning (DBCS aware)

          PCXSTR psz = _BaseClass::GetString();
          PCXSTR pszLast = NULL;

          while( *psz != 0 )
          {
              if( *psz == chTarget )
              {
                  if( pszLast == NULL )
                  {
                      pszLast = psz;
                  }
              }
              else
              {
                  pszLast = NULL;
              }
              psz = StringTraits::CharNext( psz );
          }

          if( pszLast != NULL )
          {
              // truncate at left-most matching character
              int iLast = int( pszLast-_BaseClass::GetString() );
              _BaseClass::Truncate( iLast );
          }

          return( *this );
      }

      // Remove all trailing occurrences of any of the characters in string 'pszTargets'
      _StrClass& TrimRight(PCXSTR pszTargets)
      {
          // if we're not trimming anything, we're not doing any work
          if( (pszTargets == NULL) || (*pszTargets == 0) )
          {
              return( *this );
          }

          // find beginning of trailing matches
          // by starting at beginning (DBCS aware)

          PCXSTR psz = _BaseClass::GetString();
          PCXSTR pszLast = NULL;

          while( *psz != 0 )
          {
              if( StringTraits::StringFindChar( pszTargets, *psz ) != NULL )
              {
                  if( pszLast == NULL )
                  {
                      pszLast = psz;
                  }
              }
              else
              {
                  pszLast = NULL;
              }
              psz = StringTraits::CharNext( psz );
          }

          if( pszLast != NULL )
          {
              // truncate at left-most matching character
              int iLast = int( pszLast-_BaseClass::GetString() );
              _BaseClass::Truncate( iLast );
          }

          return( *this );
      }

      // Remove all leading occurrences of character 'chTarget'
      _StrClass& TrimLeft(XCHAR chTarget)
      {
          // find first non-matching character
          PCXSTR psz = _BaseClass::GetString();

          while( chTarget == *psz )
          {
              psz = StringTraits::CharNext( psz );
          }

          if( psz != _BaseClass::GetString() )
          {
              // fix up data and length
              int iFirst = int( psz-_BaseClass::GetString() );
              PXSTR pszBuffer = _BaseClass::GetBuffer( _BaseClass::GetLength() );
              psz = pszBuffer+iFirst;
              int nDataLength = _BaseClass::GetLength()-iFirst;
#if _MSC_VER > 1200
              memmove_s( pszBuffer, (_BaseClass::GetLength()+1)*sizeof( XCHAR ),
                  psz, (nDataLength+1)*sizeof( XCHAR ) );
#else
              memmove( pszBuffer,
                  psz, (nDataLength+1)*sizeof( XCHAR ) );
#endif
              _BaseClass::ReleaseBufferSetLength( nDataLength );
          }

          return( *this );
      }

      // Remove all leading occurrences of any of the characters in string 'pszTargets'
      _StrClass& TrimLeft(PCXSTR pszTargets)
      {
          // if we're not trimming anything, we're not doing any work
          if( (pszTargets == NULL) || (*pszTargets == 0) )
          {
              return( *this );
          }

          PCXSTR psz = _BaseClass::GetString();
          while( (*psz != 0) && (StringTraits::StringFindChar( pszTargets, *psz ) != NULL) )
          {
              psz = StringTraits::CharNext( psz );
          }

          if( psz != _BaseClass::GetString() )
          {
              // fix up data and length
              int iFirst = int( psz-_BaseClass::GetString() );
              PXSTR pszBuffer = _BaseClass::GetBuffer( _BaseClass::GetLength() );
              psz = pszBuffer+iFirst;
              int nDataLength = _BaseClass::GetLength()-iFirst;
#if _MSC_VER > 1200
              memmove_s( pszBuffer, (_BaseClass::GetLength()+1)*sizeof( XCHAR ),
                  psz, (nDataLength+1)*sizeof( XCHAR ) );
#else
              memmove( pszBuffer,
                  psz, (nDataLength+1)*sizeof( XCHAR ) );
#endif
              _BaseClass::ReleaseBufferSetLength( nDataLength );
          }

          return( *this );
      }

      // Return the substring starting at index 'iFirst'
      _StrClass Mid(int iFirst) const
      {
          return( Mid( iFirst, _BaseClass::GetLength()-iFirst ) );
      }

      // Return the substring starting at index 'iFirst', with length 'nCount'
      _StrClass Mid(int iFirst, int nCount) const
      {
          // nCount is in XCHARs

          // out-of-bounds requests return sensible things
          if (iFirst < 0)
              iFirst = 0;
          if (nCount < 0)
              nCount = 0;

          if( iFirst + nCount > _BaseClass::GetLength() )
          {
              nCount = _BaseClass::GetLength()-iFirst;
          }
          if( iFirst > _BaseClass::GetLength() )
          {
              nCount = 0;
          }

          // optimize case of returning entire string
          if( (iFirst == 0) && ((iFirst+nCount) == _BaseClass::GetLength()) )
          {
              return( *this );
          }

          return( _StrClass( _BaseClass::GetString()+iFirst, nCount, GetManager() ) );
      }

      // Return the substring consisting of the rightmost 'nCount' characters
      _StrClass Right(int nCount) const
      {
          // nCount is in XCHARs
          if (nCount < 0)
              nCount = 0;

          int nLength = _BaseClass::GetLength();
          if( nCount >= nLength )
          {
              return( *this );
          }

          return( _StrClass( _BaseClass::GetString()+nLength-nCount, nCount, GetManager() ) );
      }

      // Return the substring consisting of the leftmost 'nCount' characters
      _StrClass Left(int nCount) const
      {
          // nCount is in XCHARs
          if (nCount < 0)
              nCount = 0;

          int nLength = _BaseClass::GetLength();
          if( nCount >= nLength )
          {
              return( *this );
          }

          return( _StrClass( _BaseClass::GetString(), nCount, GetManager() ) );
      }

      // Return the substring consisting of the leftmost characters in the set 'pszCharSet'
      _StrClass SpanIncluding(PCXSTR pszCharSet) const
      {
          return( Left( StringTraits::StringSpanIncluding( _BaseClass::GetString(), pszCharSet ) ) );
      }

      // Return the substring consisting of the leftmost characters not in the set 'pszCharSet'
      _StrClass SpanExcluding(PCXSTR pszCharSet) const
      {
          return( Left( StringTraits::StringSpanExcluding( _BaseClass::GetString(), pszCharSet ) ) );
      }

      // Format data using format string 'pszFormat'
      FINLINE void FCDECL Format(PCXSTR pszFormat, ...);

      // Append formatted data using format string 'pszFormat'
      FINLINE void FCDECL AppendFormat(PCXSTR pszFormat, ...);

      void AppendFormatV(
          PCXSTR pszFormat, 
          va_list args)
      {
          int nCurrentLength = _BaseClass::GetLength();
          int nAppendLength = StringTraits::GetFormattedLength( pszFormat, args );

          if (nAppendLength < 0)
              return;

          PXSTR pszBuffer = _BaseClass::GetBuffer( nCurrentLength+nAppendLength );
          StringTraits::Format( pszBuffer+nCurrentLength,
              nAppendLength+1, pszFormat, args );
          _BaseClass::ReleaseBufferSetLength( nCurrentLength+nAppendLength );
      }

      void FormatV(
          PCXSTR pszFormat, 
          va_list args)
      {
          int nLength = StringTraits::GetFormattedLength( pszFormat, args );

          if (nLength < 0)
              return;

          PXSTR pszBuffer = _BaseClass::GetBuffer( nLength );
          StringTraits::Format( pszBuffer, nLength+1, pszFormat, args );
          _BaseClass::ReleaseBufferSetLength( nLength );
      }
      size_t erase(size_t iIndex, size_t nCount = 1)
      {
          return (size_t)Delete((int)iIndex, (int)nCount);
      }
      size_t insert(size_t idx, XCHAR ch)
      {
          return (size_t)Insert((int)idx, ch);
      }
      size_t insert(size_t idx, PCXSTR psz)
      {
          return (size_t)Insert((int)idx, psz);
      }
      size_t insert(size_t idx, PCXSTR psz, size_t len)
      {
          return (size_t)Insert((int)idx, psz, (int)len);
      }

      friend _StrClass operator+(const _StrClass& str1, const _StrClass& str2)
      {
          _StrClass strResult( str1.GetManager() );

          _BaseClass::Concatenate( strResult, str1, str1.GetLength(), str2, str2.GetLength() );

          return( strResult );
      }

      friend _StrClass operator+(const _StrClass& str1, PCXSTR psz2)
      {
          _StrClass strResult( str1.GetManager() );

          _BaseClass::Concatenate( strResult, str1, str1.GetLength(), psz2, _BaseClass::StringLength( psz2 ) );

          return( strResult );
      }

      friend _StrClass operator+(PCXSTR psz1, const _StrClass& str2)
      {
          _StrClass strResult( str2.GetManager() );

          _BaseClass::Concatenate( strResult, psz1, _BaseClass::StringLength( psz1 ), str2, str2.GetLength() );

          return( strResult );
      }

#define FSTRING_CHAR_T ACHAR

      friend _StrClass operator+(const _StrClass& str1, FSTRING_CHAR_T ch2)
      {
          _StrClass strResult( str1.GetManager() );
          XCHAR chTemp = XCHAR( ch2 );

          _BaseClass::Concatenate( strResult, str1, str1.GetLength(), &chTemp, 1 );

          return( strResult );
      }

      friend _StrClass operator+(FSTRING_CHAR_T ch1, const _StrClass& str2)
      {
          _StrClass strResult( str2.GetManager() );
          XCHAR chTemp = XCHAR( ch1 );

          _BaseClass::Concatenate( strResult, &chTemp, 1, str2, str2.GetLength() );

          return( strResult );
      }

      friend _StrClass operator+(const _StrClass& str1, WCHAR ch2)
      {
          _StrClass strResult( str1.GetManager() );
          XCHAR chTemp = XCHAR( ch2 );

          _BaseClass::Concatenate( strResult, str1, str1.GetLength(), &chTemp, 1 );

          return( strResult );
      }

      friend _StrClass operator+(WCHAR ch1, const _StrClass& str2)
      {
          _StrClass strResult( str2.GetManager() );
          XCHAR chTemp = XCHAR( ch1 );

          _BaseClass::Concatenate( strResult, &chTemp, 1, str2, str2.GetLength() );

          return( strResult );
      }

      friend bool operator==(const _StrClass& str1, const _StrClass& str2)
      {
          return( str1.Compare( str2 ) == 0 );
      }

      friend bool operator==(const _StrClass& str1, PCXSTR psz2)
      {
          return( str1.Compare( psz2 ) == 0 );
      }

      friend bool operator==(PCXSTR psz1, const _StrClass& str2)
      {
          return( str2.Compare( psz1 ) == 0 );
      }

      friend bool operator==(const _StrClass& str1, PCYSTR psz2)
      {
          _StrClass str2( psz2, str1.GetManager() );

          return( str1 == str2 );
      }

      friend bool operator==(PCYSTR psz1, const _StrClass& str2)
      {
          _StrClass str1( psz1, str2.GetManager() );

          return( str1 == str2 );
      }

      friend bool operator!=(const _StrClass& str1, const _StrClass& str2 )
      {
          return( str1.Compare( str2 ) != 0 );
      }

      friend bool operator!=(const _StrClass& str1, PCXSTR psz2)
      {
          return( str1.Compare( psz2 ) != 0 );
      }

      friend bool operator!=(PCXSTR psz1, const _StrClass& str2)
      {
          return( str2.Compare( psz1 ) != 0 );
      }

      friend bool operator!=(const _StrClass& str1, PCYSTR psz2)
      {
          _StrClass str2( psz2, str1.GetManager() );

          return( str1 != str2 );
      }

      friend bool operator!=(PCYSTR psz1, const _StrClass& str2)
      {
          _StrClass str1( psz1, str2.GetManager() );

          return( str1 != str2 );
      }

      friend bool operator<(const _StrClass& str1, const _StrClass& str2)
      {
          return( str1.Compare( str2.GetString() ) < 0 );
      }

      friend bool operator<(const _StrClass& str1, PCXSTR psz2 )
      {
          return( str1.Compare( psz2.GetString() ) < 0 );
      }

      friend bool operator<(PCXSTR psz1, const _StrClass& str2)
      {
          return( str2.Compare( psz1.GetString() ) > 0 );
      }

      friend bool operator>(const _StrClass& str1, const _StrClass& str2)
      {
          return( str1.Compare( str2.GetString() ) > 0 );
      }

      friend bool operator>(const _StrClass& str1, PCXSTR psz2)
      {
          return( str1.Compare( psz2.GetString() ) > 0 );
      }

      friend bool operator>(PCXSTR psz1, const _StrClass& str2)
      {
          return( str2.Compare( psz1.GetString() ) < 0 );
      }

      friend bool operator<=(const _StrClass& str1, const _StrClass& str2)
      {
          return( str1.Compare( str2.GetString() ) <= 0 );
      }

      friend bool operator<=(const _StrClass& str1, PCXSTR psz2)
      {
          return( str1.Compare( psz2.GetString() ) <= 0 );
      }

      friend bool operator<=(PCXSTR psz1, const _StrClass& str2)
      {
          return( str2.Compare( psz1.GetString() ) >= 0 );
      }

      friend bool operator>=(const _StrClass& str1, const _StrClass& str2)
      {
          return( str1.Compare( str2.GetString() ) >= 0 );
      }

      friend bool operator>=(const _StrClass& str1, PCXSTR psz2)
      {
          return( str1.Compare( psz2.GetString() ) >= 0 );
      }

      friend bool operator>=(PCXSTR psz1, const _StrClass& str2)
      {
          return( str2.Compare( psz1.GetString() ) <= 0 );
      }

      friend bool operator==(XCHAR ch1, const _StrClass& str2)
      {
          return( (str2.GetLength() == 1) && (str2[0] == ch1) );
      }

      friend bool operator==(const _StrClass& str1, XCHAR ch2)
      {
          return( (str1.GetLength() == 1) && (str1[0] == ch2) );
      }

      friend bool operator!=(XCHAR ch1, const _StrClass& str2)
      {
          return( (str2.GetLength() != 1) || (str2[0] != ch1) );
      }

      friend bool operator!=(const _StrClass& str1, XCHAR ch2)
      {
          return( (str1.GetLength() != 1) || (str1[0] != ch2) );
      }

private:
    bool CheckImplicitLoad(const void* pv)
    {
        bool bRet = false;
        return( bRet );
    }
};

// Format data using format string 'pszFormat'
template< typename BaseType, class StringTraits >
FINLINE void FCDECL FUI_StringT<BaseType, StringTraits>::Format(PCXSTR pszFormat, ...)
{
    va_list argList;
    va_start( argList, pszFormat );
    FormatV( pszFormat, argList );
    va_end( argList );
}

// Append formatted data using format string 'pszFormat'
template< typename BaseType, class StringTraits >
FINLINE void FCDECL FUI_StringT<BaseType, StringTraits>::AppendFormat(PCXSTR pszFormat,  ...)
{
    va_list argList;
    va_start( argList, pszFormat );

    AppendFormatV( pszFormat, argList );

    va_end( argList );
}


typedef FUI_StringT< ACHAR, FUI_StringTraitsCRT< ACHAR > > FUI_StringA; // 字符串对象
typedef FUI_StringT< WCHAR, FUI_StringTraitsCRT< WCHAR > > FUI_StringW; // 字符串对象
typedef FUI_Vector< FUI_StringA >  FUI_VStringA;           // 字符串数组
typedef FUI_Vector< FUI_StringW >  FUI_VStringW;           // 字符串数组

#ifdef UNICODE
typedef FUI_StringW             FUI_String;
typedef FUI_VStringW            FUI_VString;
#else
typedef FUI_StringA             FUI_String;
typedef FUI_VStringA            FUI_VString;
#endif

#ifdef WIN32
typedef FUI_StringW             FFUI_String;
typedef FUI_VStringW            FFUI_VString;
#else
typedef FUI_StringA             FFUI_String;
typedef FUI_VStringA            FFUI_VString;
#endif


#endif
